package com.whmall.service.crm.module.company.service;

import cn.hutool.core.util.BooleanUtil;
import com.whmall.common.enumeration.YesNoEnum;
import com.whmall.component.entity.dto.QueryManagerRspDTO;
import com.whmall.component.enumeration.PowerEnum;
import com.whmall.service.crm.module.company.dao.CompanyMapper;
import com.whmall.service.crm.module.company.entity.CompanyDO;
import com.whmall.service.system.module.employee.enumeration.EmployeeStatusEnum;
import com.whmall.service.system.module.employee.enumeration.PositionEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 公司相关算法
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CompanyAlgorithm {

    private final CompanyMapper companyMapper;

    private final RedisTemplate<String, Long> longRedisTemplate;

    /**
     * 初始化负责人（撮合等）
     *
     * @param companyId       公司ID
     * @param candidates      候选人列表
     * @param traderIdDefault 默认撮合
     */
    public void initManagers(
            Long companyId, List<QueryManagerRspDTO> candidates,
            Long traderIdDefault, Long bdIdDefault, Long csIdDefault, Long purchaserIdDefault, Long vmIdDefault) {
        Assert.notNull(companyId, "公司ID不能为空");
        if (CollectionUtils.isEmpty(candidates)) {
            return;
        }

        CompanyDO company = companyMapper.selectById(companyId);
        Assert.notNull(company, "未找到公司");

        Long traderId = null;
        Long purchaserId = null;
        Long backPurchaserId = null;
        Long vmId = null;
        Long csId = null;
        String traderDepartmentPath = null;
        String purchaserDepartmentPath = null;
        String vmDepartmentPath = null;
        String csDepartmentPath = null;
        String bdDepartmentPath = null;

        Map<Long, QueryManagerRspDTO> candidateMap = candidates.stream()
                .collect(Collectors.toMap(QueryManagerRspDTO::getId, e -> e, (v1, v2) -> v1));

        //买家
        if (company.getIsCustomer() == YesNoEnum.YES.getCode()) {
            //撮合
            /*
             * 撮合：
             * ②判断员工职位属于“撮合交易员”，状态属于“在职”，非“休假”状态，是否分配新客户为“是”平均分配撮合人员。
             */
            List<Long> employees = candidates.stream().filter(e ->
                    e.getPower().equals(PowerEnum.TRADER.getValue())
                            && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
                            && e.getIsCanc()
            ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
            traderId = traderIdDefault != null && traderIdDefault > 0 ?
                    traderIdDefault :
                    getOneManager(PositionEnum.TRADER.getValue(), true, null, employees);
            if (traderId != null) {
                Assert.notNull(candidateMap.get(traderId), "员工数据异常");
                traderDepartmentPath = candidateMap.get(traderId).getDepartmentPath();
            }
        }

        //卖家
        if (company.getIsSupplier() == YesNoEnum.YES.getCode()) {
            //采购
            /*
             * 采购：
             * 判断职位属于“采购专员”，状态属于“在职”（包括“休假”），是否分配新客户为“是”，平均分配采购人员。
             */
            List<Long> employees = candidates.stream().filter(e ->
                    e.getPower().equals(PowerEnum.PURCHASER.getValue())
                            && e.getIsCanc()
            ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
            purchaserId = purchaserIdDefault != null && purchaserIdDefault != 0 ?
                    purchaserIdDefault :
                    getOneManager(PositionEnum.PURCHASER.getValue(), true, null, employees);
            if (purchaserId != null) {
                Assert.notNull(candidateMap.get(purchaserId), "员工数据异常");
                purchaserDepartmentPath = candidateMap.get(purchaserId).getDepartmentPath();
            }
            //备选采购
            /*
             * 备选采购：
             * 按天计算，分配给当天订单量最少的采购人员。此处暂不实现，等待订单系统提供接口
             */
            backPurchaserId = getOneManager(PositionEnum.PURCHASER.getValue(), true, null, employees);
            int loop = 0;
            while (Objects.equals(backPurchaserId, purchaserId)) {
                // 如果采购和备选采购是一个人，再次分配
                backPurchaserId = getOneManager(PositionEnum.PURCHASER.getValue(), true, null, employees);
                loop += 1;
                if (loop >= 5) {
                    log.error("循环分配备选采购5次，备选采购和采购仍然是同一个人：{} = {}，其中，候选人为：{}",
                            backPurchaserId, purchaserId, employees);
                    break;
                }
            }
            //供管
            /*
             * 供管：
             * 判断员工职位属于“供应商开发与管理专员”，状态属于“在职”，非“休假”状态，平均分配撮合人员
             */
            employees = candidates.stream().filter(e ->
                    e.getPower().equals(PowerEnum.VENDOR_MANAGER.getValue())
                            && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
                            && e.getIsCanc()
            ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
            vmId = vmIdDefault != null && vmIdDefault != 0 ?
                    vmIdDefault :
                    getOneManager(PositionEnum.VENDOR_MANAGER.getValue(), true, null, employees);
            if (vmId != null) {
                Assert.notNull(candidateMap.get(vmId), "员工数据异常");
                vmDepartmentPath = candidateMap.get(vmId).getDepartmentPath();
            }
        }

        //客服
        /**客服（买家、卖家都需要分配）：
         * 平均分配
         */
        List<Long> employees = candidates.stream().filter(e ->
                e.getPower().equals(PowerEnum.CUSTOMER_SERVICE.getValue())
                        && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
        ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
        csId = csIdDefault != null && csIdDefault != 0L ?
                csIdDefault :
                getOneManager(PositionEnum.CUSTOMER_SERVICE.getValue(), true, null, employees);
        if (csId != null) {
            Assert.notNull(candidateMap.get(csId), "员工数据异常");
            csDepartmentPath = candidateMap.get(csId).getDepartmentPath();
        }

        // 默认分配BD
        if (null != bdIdDefault && bdIdDefault > 0) {
            bdDepartmentPath = candidateMap.get(bdIdDefault).getDepartmentPath();
        }

        //更新负责人
        CompanyDO companyUpdate = CompanyDO.builder()
                .id(companyId)
                .traderId(traderId)
                .purchaserId(purchaserId)
                .backPurchaserId(backPurchaserId)
                .vmId(vmId)
                .csId(csId)
                .bdId(bdIdDefault)
                .traderDepartmentPath(traderDepartmentPath)
                .purchaserDepartmentPath(purchaserDepartmentPath)
                .vmDepartmentPath(vmDepartmentPath)
                .csDepartmentPath(csDepartmentPath)
                .bdDepartmentPath(bdDepartmentPath)
                .build();
        int count = companyMapper.updateById(companyUpdate);
        Assert.isTrue(count == 1, "未找到公司");
    }


    /**
     * 新公司初始化负责人（撮合等）公司还未创建
     *
     * @param companyDO       新建公司
     * @param candidates      候选人列表
     * @param traderIdDefault 默认撮合
     */
    public void newDataInitManagers(
            CompanyDO companyDO, List<QueryManagerRspDTO> candidates,
            Long traderIdDefault, Long bdIdDefault, Long csIdDefault, Long purchaserIdDefault, Long vmIdDefault,
            boolean isNewCustomer) {
        if (CollectionUtils.isEmpty(candidates)) {
            return;
        }

        Map<Long, QueryManagerRspDTO> candidateMap = candidates.stream()
                .collect(Collectors.toMap(QueryManagerRspDTO::getId, e -> e, (v1, v2) -> v1));

        //
        // 买家
        if (companyDO.getIsCustomer() == YesNoEnum.YES.getCode()) {
            // 撮合  判断员工职位属于“撮合交易员”，状态属于“在职”，非“休假”状态，是否分配新客户为“是”平均分配撮合人员
            List<Long> employees = candidates.stream().filter(e ->
                    e.getPower().equals(PowerEnum.TRADER.getValue())
                            && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
                            && (!BooleanUtil.isTrue(isNewCustomer) || BooleanUtil.isTrue(e.getIsCanc()))
                            && (!BooleanUtil.isFalse(isNewCustomer) || BooleanUtil.isTrue(e.getIsCaocno()))
            ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
            // 这里批量新增的时候按照老客户处理
            companyDO.setTraderId(traderIdDefault != null && traderIdDefault > 0
                    ? traderIdDefault
                    : getOneManager(PositionEnum.TRADER.getValue(), isNewCustomer, null, employees));
            if (companyDO.getTraderId() != null) {
                Assert.isTrue(null != candidateMap.get(companyDO.getTraderId()), "员工数据异常");
                companyDO.setTraderDepartmentPath(candidateMap.get(companyDO.getTraderId()).getDepartmentPath());
            }
        }

        //
        // 卖家
        if (null != companyDO.getIsSupplier() && companyDO.getIsSupplier() == YesNoEnum.YES.getCode()) {
            // 采购 判断职位属于“采购专员”，状态属于“在职”（包括“休假”），是否分配新客户为“是”，平均分配采购人员
            List<Long> employees = candidates.stream().filter(e ->
                    e.getPower().equals(PowerEnum.PURCHASER.getValue())
                            && e.getIsCanc()
            ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
            companyDO.setPurchaserId(purchaserIdDefault != null && purchaserIdDefault != 0 ?
                    purchaserIdDefault :
                    getOneManager(PositionEnum.PURCHASER.getValue(), isNewCustomer, null, employees));
            if (companyDO.getPurchaserId() != null) {
                Assert.isTrue(null != candidateMap.get(companyDO.getPurchaserId()), "员工数据异常");
                companyDO.setPurchaserDepartmentPath(candidateMap.get(companyDO.getPurchaserId()).getDepartmentPath());
            }
            // 备选采购 按天计算，分配给当天订单量最少的采购人员。此处暂不实现，等待订单系统提供接口
            // 供管 判断员工职位属于“供应商开发与管理专员”，状态属于“在职”，非“休假”状态，平均分配撮合人员
            employees = candidates.stream().filter(e ->
                    e.getPower().equals(PowerEnum.VENDOR_MANAGER.getValue())
                            && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
                            && e.getIsCanc()
            ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
            companyDO.setVmId(
                    vmIdDefault != null && vmIdDefault != 0 ?
                            vmIdDefault :
                            getOneManager(PositionEnum.VENDOR_MANAGER.getValue(), isNewCustomer, null, employees));
            if (companyDO.getVmId() != null) {
                Assert.isTrue(null != candidateMap.get(companyDO.getVmId()), "员工数据异常");
                companyDO.setVmDepartmentPath(candidateMap.get(companyDO.getVmId()).getDepartmentPath());
            }
        }

        //
        // 客服（买家、卖家都需要分配）平均分配
        List<Long> employees = candidates.stream().filter(e ->
                e.getPower().equals(PowerEnum.CUSTOMER_SERVICE.getValue())
                        && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
        ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
        companyDO.setCsId(
                csIdDefault != null && csIdDefault != 0 ?
                        csIdDefault :
                        getOneManager(PositionEnum.CUSTOMER_SERVICE.getValue(), isNewCustomer, null, employees));
        if (companyDO.getCsId() != null) {
            Assert.isTrue(null != candidateMap.get(companyDO.getCsId()), "员工数据异常");
            companyDO.setCsDepartmentPath(candidateMap.get(companyDO.getCsId()).getDepartmentPath());
        }

        // 默认分配BD
        if (null != bdIdDefault && bdIdDefault > 0) {
            companyDO.setBdDepartmentPath(candidateMap.get(bdIdDefault).getDepartmentPath());
        }
    }


    /**
     * 获取一个负责人
     * <br>平均分配原则
     *
     * @param targetPositionId 目标职位ID
     * @param isNewCustomer    是否新客户（新建公司/新建询盘且公司是新客户）
     * @param params           其他参数
     * @param candidates       候选人列表
     * @return 负责人
     */
    public Long getOneManager(Integer targetPositionId, Boolean isNewCustomer, Map<Object, Object> params,
                              List<Long> candidates) {
        /*
         * 从队列中取队首作为负责人，如果此人不在查出来的候选人列表中，说明已失效需要从队列移出;
         * 如果候选人列表有新的人员，则添加到队尾
         */
        String key = null;
        if (PositionEnum.TRADER.getValue().equals(targetPositionId)) {
            key = "@queue_company_trader_" + (isNewCustomer ? "nc" : "ocno");
        } else if (PositionEnum.PURCHASER.getValue().equals(targetPositionId)) {
            key = "@queue_company_purchaser";
        } else if (PositionEnum.PURCHASER_BACK.getValue().equals(targetPositionId)) {
            key = "@queue_company_back_purchaser";
        } else if (PositionEnum.VENDOR_MANAGER.getValue().equals(targetPositionId)) {
            key = "@queue_company_vm";
        } else if (PositionEnum.CUSTOMER_SERVICE.getValue().equals(targetPositionId)) {
            key = "@queue_company_cs";
        } else if (PositionEnum.BD.getValue().equals(targetPositionId)) {
            key = "@queue_company_bd";
        } else if (PositionEnum.CONTROL.getValue().equals(targetPositionId)) {
            key = "@queue_company_control_" + params.get("province");
        }
        Assert.notNull(key, "参数错误");
        Set<Long> candidateSet, queueSet;
        if (candidates == null) {
            candidateSet = new HashSet<>(1);
        } else {
            candidateSet = new HashSet<>(candidates);
        }
        //设置有效时间，使之有机会重新初始化，增加容错性
        if (Boolean.FALSE.equals(longRedisTemplate.hasKey(key))) {
            longRedisTemplate.opsForList().leftPush(key, -1L);
            longRedisTemplate.expire(key, 30L, TimeUnit.DAYS);
        }
        List<Long> queue = longRedisTemplate.opsForList().range(key, 0, -1);
        if (queue == null) {
            queueSet = new HashSet<>(1);
        } else {
            queueSet = new HashSet<>(queue);
            //去重。在并发（新增的人员入队）的情况下数据可能重复，导致分配不均
            queueSet.forEach(queue::remove);
            String finalKey = key;
            queue.forEach(e -> {
                longRedisTemplate.opsForList().remove(finalKey, 1, e);
            });
        }
        //新增的人员入队
        Set<Long> candidateSetNew = new HashSet<>(candidateSet);
        candidateSetNew.removeAll(queueSet);
        if (!candidateSetNew.isEmpty()) {
            longRedisTemplate.opsForList().leftPushAll(key, candidateSetNew);
        }
        //取队首作为负责人
        while (true) {
            Long mrRight = longRedisTemplate.opsForList().rightPopAndLeftPush(key, key);
            if (mrRight == null) {
                return null;
            } else if (candidateSet.contains(mrRight)) {//候选人有效
                return mrRight;
            } else {//候选人失效，则从队列移出
                longRedisTemplate.opsForList().remove(key, 999, mrRight);
            }
        }
    }

}
